#include "XOpenFile.h"

#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>

#ifdef _WIN32

#include "mmap-windows.h"
#include <windows.h>
#include <io.h>

struct FilePointer *XOpenFile(const char *file) {
    void *data = NULL;
    struct FilePointer *result = NULL;
    unsigned long long int fileSize = 0;
    HANDLE fp = CreateFile(file, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (fp == INVALID_HANDLE_VALUE) {
        return NULL;
    }

    DWORD dwFileSizeHigh = 0;
    DWORD dwFileSize = GetFileSize(fp, &dwFileSizeHigh);
    fileSize = (unsigned long long int)dwFileSize + (((unsigned long long int)dwFileSizeHigh) << 32);

    int fd = _open_osfhandle((intptr_t)fp, _O_BINARY | _O_RDONLY);

    data = mmap(NULL, fileSize, PROT_READ, MAP_SHARED, fd, 0);
    if (data == NULL || MAP_FAILED == data) {
        _close(fd);
        CloseHandle(fp);
        return NULL;
    }

    result = (struct FilePointer*)malloc(sizeof(struct FilePointer));
    if (result == NULL) {
        munmap(data, fileSize);
        _close(fd);
        CloseHandle(fp);
        return NULL;
    }

    result->_fd = fd;
    result->data = data;
    result->size = fileSize;

    return result;
}

void XCloseFile(struct FilePointer *filePointer) {
    if (NULL != filePointer) {
        munmap(filePointer->data, filePointer->size);
        HANDLE fp = (HANDLE)_get_osfhandle(filePointer->_fd);
        _close(filePointer->_fd);
        CloseHandle(fp);
        free(filePointer);
    }
}

#endif // _WIN32

#ifdef __linux__

#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>

struct FilePointer *XOpenFile(const char *file) {
    struct FilePointer *result = (struct FilePointer*)malloc(sizeof(struct FilePointer));
    if (NULL == result) {
        return NULL;
    }
    result->_fd = open(file, O_RDONLY);
    if (result->_fd < 0) {
        free(result);
        return NULL;
    }

    struct stat st;
    if (0 != fstat(result->_fd, &st)) {
        close(result->_fd);
        free(result);
        return NULL;
    }
    result->data = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, result->_fd, 0);
    if (MAP_FAILED == result->data) {
        close(result->_fd);
        free(result);
        return NULL;
    }
    result->size = st.st_size;

    return result;
}

void XCloseFile(struct FilePointer *filePointer) {
    if (NULL != filePointer) {
        munmap(filePointer->data, filePointer->size);
        close(filePointer->_fd);
        free(filePointer);
        filePointer = NULL;
    }
}

#endif // __linux__
